// Timer Interrupt handler


#include "include/types.h"
#include "include/param.h"
#include "include/riscv.h"
#include "include/defs.h"
#include "include/sbi.h"
#include "include/memlayout.h"
#include "include/uarths.h"
#include "include/proc.h"
#include "include/times.h"
#include "include/errno.h"
#include "include/debug.h"
uint ticks;

void timerinit() {
    // enable supervisor-mode timer interrupts.
    w_sie(r_sie() | SIE_STIE);
    set_next_timeout();
    #ifdef DEBUG
    printf("timerinit\n");
    #endif
}

void
set_next_timeout() {
    // There is a very strange bug,
    // if comment the `printf` line below
    // the timer will not work.

    // this bug seems to disappear automatically
    // printf("");
    sbi_set_timer(r_time() + INTERVAL);
}

void timer_tick(int flag) {
    acquire(&tickslock);
    ticks++;
    wakeup(&ticks);
    release(&tickslock);
    set_next_timeout();
}

uint64 sys_utimensat(void)
{
  /*
  int utimensat(int dirfd, const char *pathname,
              const struct timespec times[2], int flags);
    */
  int dirfd, flags;
  uint64 addr;
  char pathname[MAXPATH];
  struct timespec times[2];
  if (argint(0, &dirfd) < 0 || argstr(1, pathname, MAXPATH) < 0 || argaddr(2, &addr) < 0 || argint(3, &flags) < 0) {
    __debug_error("sys_utimensat", "args error\n");
    return -1;
  }
  if (dirfd != AT_FDCWD) {
    // TODO support relative dir
    __debug_info("sys_utimensat", "now only support dirfd = AT_FDCWD\n");
  }
  if (copyin(myproc()->pagetable, (char *)times, addr, sizeof(times)) < 0) {
    __debug_error("sys_utimensat", "copyin error\n");
    return -1;
  }
  struct dirent * ep;
  if((ep = ename(pathname)) == 0) {
    /*This should not be an error, since sometimes we want utimes first, if failed, then openat with O_CREAT flag*/
    __debug_info("sys_utimensat", "can't find %s\n", pathname);
    // TODO understand usage of error code
    return -ENOENT;
  }
  // TODO now only copy timespec info, but haven't change file timestamps
  return 0;
}

uint64 sys_clock_gettime(void)
{
  int clockid;
  uint64 tp;
  if (argint(0, &clockid) < 0 || argaddr(1, &tp) < 0) {
    __debug_error("sys_clock_gettime", "arg error\n");
    return -1;
  }
  // TODO now only copy out a fake value
  struct timespec temp = {1000, 1000};
  if (copyout(myproc()->pagetable, tp, (char *)&temp, sizeof(temp)) < 0) {
    __debug_error("sys_clock_gettime", "copyout error\n");
    return -1;
  }
  return 0;
}

uint64 sys_gettimeofday(void) {
  // test-user: syscall(SYS_gettimeofday, struct timespec * ts, 0);
  //                                                0           1
  uint64 addr;
  if (argaddr(0, &addr) < 0) {
    return -1;
  }
  struct timespec * tbuf = (struct timespec *)addr;
  struct proc *p = myproc();
  uint64 sec = ticks;
  uint64 usec = 0;
  if (addr) {
    if (copyout(p->pagetable, (uint64)&(tbuf -> sec), (char *)&sec, sizeof(sec)) < 0) {
      return -1;
    }
    if (copyout(p->pagetable, (uint64)&(tbuf -> usec), (char *)&usec, sizeof(usec)) < 0) {
      return -1;
    }
  } 
  return 0;
}

uint64 sys_times(void) {
  uint64 addr;
  if (argaddr(0, &addr) < 0) {
    return -1;
  }
  struct tms * tbuf = (struct tms *)addr;
  struct proc *p = myproc();
  if (addr) {
    if (copyout(p->pagetable, (uint64)&(tbuf -> tms_utime), (char *)&(p->utime), sizeof(p->utime)) < 0) {
      return -1;
    }
    if (copyout(p->pagetable, (uint64)&(tbuf -> tms_stime), (char *)&(p->stime), sizeof(p->stime)) < 0) {
      return -1;
    }
    if (copyout(p->pagetable, (uint64)&(tbuf -> tms_cutime), (char *)&(p->cutime), sizeof(p->cutime)) < 0) {
      return -1;
    }
    if (copyout(p->pagetable, (uint64)&(tbuf -> tms_cstime), (char *)&(p->cstime), sizeof(p->cstime)) < 0) {
      return -1;
    }
  }
  return ticks;
}